﻿using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Globalization;
using System.Net.WebSockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;

using CodeSignServerMaster.Contexts;

using Microsoft.AspNetCore.Connections;

namespace CodeSignServerMaster
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers();
            builder.WebHost.UseKestrel(options =>
            {
                // 无限制请求体大小
                options.Limits.MaxRequestBodySize = null;
            });

            var app = builder.Build();
            app.Urls.Add("http://0.0.0.0:57562");

            // Configure the HTTP request pipeline.

            app.UseWebSockets(new WebSocketOptions()
            {
                KeepAliveInterval = TimeSpan.FromSeconds(1),
                //KeepAliveTimeout = TimeSpan.FromSeconds(10),
            });

            var signSlaveList = new List<SignSlave>();

            // 不能通过 Websocket 传输文件，否则会导致网关内存占用过高。当前网关是先完全接收，然后再打到本服务。通过日志可见，瞬间获取 200MB 的传输，但在客户端上传过程中，需要等待好久之后，才能到达本服务的控制器。证明内容是先被网关接收，然后再转发
            //app.Use(async (context, next) =>
            //{
            //    if (context.Request.Path != "/task")
            //    {
            //        await next(context);
            //        return;
            //    }

            //    if (!context.WebSockets.IsWebSocketRequest)
            //    {
            //        context.Response.StatusCode = StatusCodes.Status400BadRequest;
            //        await context.Response.StartAsync();
            //        await context.Response.CompleteAsync();
            //        return;
            //    }

            //    WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
            //    var arraySegment = new ArraySegment<byte>(new byte[10240]);
            //    var result = await webSocket.ReceiveAsync(arraySegment, CancellationToken.None);
            //    var nameSpan = arraySegment.AsSpan(0, result.Count);
            //    var name = Encoding.UTF8.GetString(nameSpan);

            //    var semaphoreSlim = new SemaphoreSlim(1);

            //    var signSlave = new SignSlave(name, webSocket, semaphoreSlim);
            //    lock (signSlaveList)
            //    {
            //        signSlaveList.Add(signSlave);
            //    }

            //    while (webSocket.State == WebSocketState.Open)
            //    {
            //        bool beBreak = false;
            //        if (await signSlave.SemaphoreSlim.WaitAsync(0))
            //        {
            //            try
            //            {
            //                MessageType messageType = new MessageType()
            //                {
            //                    Type = 1,
            //                    Header = MessageType.DefaultHeader
            //                };

            //                var memory = arraySegment.AsMemory(0, messageType.HeadLength);
            //                memory.Span.Clear();

            //                MemoryMarshal.Write(memory.Span, in messageType);

            //                await webSocket.SendAsync(memory, WebSocketMessageType.Binary,
            //                    WebSocketMessageFlags.EndOfMessage, CancellationToken.None);

            //                using var cancellationTokenSource =
            //                    new CancellationTokenSource(TimeSpan.FromSeconds(10));
            //                try
            //                {
            //                    ValueWebSocketReceiveResult result2 =
            //                        await webSocket.ReceiveAsync(memory, cancellationTokenSource.Token);
            //                    var responseMessageType =
            //                        MemoryMarshal.Read<MessageType>(memory.Span.Slice(0, result2.Count));
            //                    Debug.Assert(responseMessageType.Type == 3);
            //                }
            //                catch (OperationCanceledException e)
            //                {
            //                    Console.WriteLine(e);
            //                    beBreak = true;
            //                    break;
            //                }
            //                catch (WebSocketException e)
            //                {
            //                    if (e.InnerException is ConnectionResetException resetException)
            //                    {
            //                        Debug.Assert((uint) resetException.HResult == 0x80131620);
            //                        beBreak = true;
            //                        break;
            //                    }
            //                }
            //            }
            //            finally
            //            {
            //                if (beBreak)
            //                {
            //                    lock (signSlaveList)
            //                    {
            //                        signSlaveList.Remove(signSlave);
            //                    }
            //                }

            //                signSlave.SemaphoreSlim.Release();
            //            }
            //        }

            //        await Task.Delay(TimeSpan.FromMilliseconds(100));
            //    }

            //    lock (signSlaveList)
            //    {
            //        signSlaveList.Remove(signSlave);
            //    }

            //    signSlave.Dispose();
            //});

            app.MapGet("/alive", () => DateTime.Now.ToString(CultureInfo.InvariantCulture));

            var logger = app.Services.GetRequiredService<ILogger<Program>>();

            var signTaskInfoList = new List<SignTaskInfo>();

            int GetSignSlaveCount()
            {
                lock (signSlaveList)
                {
                    return signSlaveList.Count(t => (DateTimeOffset.Now - t.VisitTime) < TimeSpan.FromMinutes(1));
                }
            }

            app.MapPost("/signapp", (SignTaskRequest request) =>
            {
                logger.LogInformation($"[SignApp] TraceId={request.TraceId};FileUrl={request.FileUrl};SignName={request.SignName};CurrentSlaveCount={GetSignSlaveCount()}");

                var tcs = new TaskCompletionSource<SignTaskResponse>();
                lock (signTaskInfoList)
                {
                    signTaskInfoList.Add(new SignTaskInfo(request, tcs));
                }

                return tcs.Task;
            });

            app.Map("/fetch", async (Microsoft.AspNetCore.Http.HttpContext context) =>
            {
                WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
                var buffer = ArrayPool<byte>.Shared.Rent(102400);
                context.Response.RegisterForDispose(() => ArrayPool<byte>.Shared.Return(buffer));

                var webSocketReceiveResult = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
                var messageSpan = buffer.AsSpan(0, webSocketReceiveResult.Count);
                var message = Encoding.UTF8.GetString(messageSpan);
                var signSlaveInfo = JsonSerializer.Deserialize<SignSlaveInfo>(message);

                logger.LogInformation($"收到签名服务器信息: {signSlaveInfo}");
                lock (signSlaveList)
                {
                    if (signSlaveInfo is not null)
                    {
                        // 预期签名服务器数量不多，直接线性查找
                        signSlaveList.RemoveAll(t => t.SignSlaveInfo.Name == signSlaveInfo.Name || (DateTimeOffset.Now - t.VisitTime) > TimeSpan.FromMinutes(1));

                        signSlaveList.Add(new SignSlave(signSlaveInfo, DateTimeOffset.Now));
                    }
                }

                SignTaskInfo? signTaskInfo = null;
                for (int i = 0; i < 1000; i++)
                {
                    lock (signTaskInfoList)
                    {
                        if (signTaskInfoList.Count > 0)
                        {
                            signTaskInfo = signTaskInfoList[0];
                            signTaskInfoList.RemoveAt(0);
                            break;
                        }
                    }

                    await Task.Delay(10);
                }

                if (signTaskInfo is null)
                {
                    logger.LogInformation($"当前任务空闲，无任何签名任务可以给 {signSlaveInfo?.Name}");
                }
                else
                {
                    logger.LogInformation($"调度签名任务给 {signSlaveInfo?.Name} {signTaskInfo}");
                }

                var fetchSignTaskRequest = new FetchSignTaskRequest(signTaskInfo?.Request, "Fetch sign task");

                var json = JsonSerializer.Serialize(fetchSignTaskRequest);
                var jsonByteList = Encoding.UTF8.GetBytes(json);
                await webSocket.SendAsync(jsonByteList, WebSocketMessageType.Text, true, CancellationToken.None);

                var arraySegment = new ArraySegment<byte>(new byte[10240]);
                var result = await webSocket.ReceiveAsync(arraySegment, CancellationToken.None);
                messageSpan = arraySegment.AsSpan(0, result.Count);
                message = Encoding.UTF8.GetString(messageSpan);
                var fetchSignTaskResponse = JsonSerializer.Deserialize<FetchSignTaskResponse>(message);

                if (signTaskInfo != null)
                {
                    signTaskInfo.ResponseTaskCompletionSource.SetResult(fetchSignTaskResponse?.SignTaskResponse ?? new SignTaskResponse(signTaskInfo.Request.TraceId, "", "Can not find result."));
                }
            });

            //app.Map("/sign", async (Microsoft.AspNetCore.Http.HttpContext content) =>
            //{
            //    var contentLength = content.Request.Headers.ContentLength;
            //    if (contentLength == null || contentLength <= 0)
            //    {
            //        await FastFail(StatusCodes.Status411LengthRequired, "请求体不能为空");
            //        return;
            //    }

            //    // Get a free slave
            //    SignSlave? freeSlave = null;
            //    lock (signSlaveList)
            //    {
            //        foreach (var slave in signSlaveList)
            //        {
            //            if (slave.SemaphoreSlim.Wait(0))
            //            {
            //                freeSlave = slave;
            //                break;
            //            }
            //        }
            //    }

            //    if (freeSlave == null)
            //    {
            //        lock (signSlaveList)
            //        {
            //            if (signSlaveList.Count == 0)
            //            {
            //            }
            //            else
            //            {
            //                freeSlave = signSlaveList[Random.Shared.Next(signSlaveList.Count)];
            //            }
            //        }

            //        if (freeSlave == null)
            //        {
            //            await FastFail(StatusCodes.Status503ServiceUnavailable, $"无可用签名服务器");
            //            return;
            //        }

            //        await freeSlave.SemaphoreSlim.WaitAsync();
            //    }

            //    content.Response.StatusCode = 200;
            //    await content.Response.StartAsync();

            //    var buffer = ArrayPool<byte>.Shared.Rent(102400);
            //    try
            //    {
            //        // Read content to byte array

            //        Stream requestContentStream = content.Request.Body;

            //        // Header Length
            //        // 默认 C# 就是小端
            //        var messageType = new MessageType()
            //        {
            //            Type = 2,
            //            Header = MessageType.DefaultHeader
            //        };
            //        Memory<byte> memory = buffer.AsMemory(0, messageType.HeadLength);
            //        memory.Span.Clear();
            //        MemoryMarshal.Write(memory.Span, in messageType);

            //        var webSocket = freeSlave.WebSocket;
            //        await webSocket.SendAsync(memory,
            //            WebSocketMessageType.Binary, WebSocketMessageFlags.EndOfMessage, CancellationToken.None);

            //        var stopwatch = Stopwatch.StartNew();
            //        long totalReadCount = 0;
            //        long secondReadCount = 0;
            //        while (true)
            //        {
            //            // 转发请求的内容
            //            memory = buffer.AsMemory();
            //            var readCount = await requestContentStream.ReadAsync(memory);
            //            if (readCount == 0)
            //            {
            //                break;
            //            }

            //            totalReadCount += readCount;
            //            secondReadCount += readCount;

            //            var messageFlags = WebSocketMessageFlags.None;

            //            if (contentLength > 0 && totalReadCount >= contentLength)
            //            {
            //                messageFlags = WebSocketMessageFlags.EndOfMessage;
            //            }

            //            await webSocket.SendAsync(memory.Slice(0, readCount), WebSocketMessageType.Binary,
            //                messageFlags, CancellationToken.None);

            //            if (stopwatch.Elapsed.TotalSeconds > 1)
            //            {
            //                var speed = secondReadCount / stopwatch.Elapsed.TotalSeconds;
            //                secondReadCount = 0;
            //                stopwatch.Restart();
            //                logger.LogInformation($"签名服务器 {freeSlave.Name} 上传速度 {speed / 1024.0 / 1024.0:0.##} MB/s");
            //            }
            //        }

            //        // System.ArgumentNullException:“Value cannot be null. (Parameter 'buffer.Array')”
            //        //await webSocket.SendAsync([], WebSocketMessageType.Binary, endOfMessage: true, CancellationToken.None);

            //        while (true)
            //        {
            //            var receiveResult = await webSocket.ReceiveAsync(memory, CancellationToken.None);

            //            await content.Response.BodyWriter.WriteAsync(memory.Slice(0, receiveResult.Count));

            //            if (receiveResult.EndOfMessage)
            //            {
            //                break;
            //            }
            //        }
            //    }
            //    catch (Exception e)
            //    {
            //        logger.LogWarning(e, "签名服务失败");
            //    }
            //    finally
            //    {
            //        freeSlave.SemaphoreSlim.Release();
            //        ArrayPool<byte>.Shared.Return(buffer);
            //        await content.Response.CompleteAsync();
            //    }

            //    return;

            //    async Task FastFail(int statusCode, string errorMessage)
            //    {
            //        if (contentLength > 0)
            //        {
            //            // 读取请求体，避免客户端异常

            //            // 不能通过 CompleteAsync 设置读取完成，此时会让客户端无法完成传输
            //            //await content.Request.BodyReader.CompleteAsync();
            //            using var emptyStream = new EmptyStream();
            //            await content.Request.Body.CopyToAsync(emptyStream);
            //        }

            //        content.Response.StatusCode = statusCode;
            //        await content.Response.StartAsync();
            //        await content.Response.WriteAsync(errorMessage);
            //        await content.Response.CompleteAsync();
            //    }
            //});

            app.Run();
        }
    }
}

record SignSlave(SignSlaveInfo SignSlaveInfo, DateTimeOffset VisitTime)
{
}

record SignTaskInfo(SignTaskRequest Request, TaskCompletionSource<SignTaskResponse> ResponseTaskCompletionSource);

static class HttpHelper
{
    public static void RegisterForDispose(this Microsoft.AspNetCore.Http.HttpResponse response, Action disposeAction)
    {
        response.RegisterForDispose(new DelegateDisposable(disposeAction));
    }

    class DelegateDisposable(Action disposeAction) : IDisposable
    {
        public void Dispose()
        {
            disposeAction();
        }
    }
}